package com.codeczx.myretrofitdemo.retrofit;

import android.util.Log;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;

import okhttp3.*;
import okhttp3.Request;

/**
 * Created by codeczx on 2018/3/7.
 */

/**
 * 配置网络请求对象
 */
public class ServiceMethod<R, T> {

    final okhttp3.Call.Factory callFactory;
    private final Converter<ResponseBody,R> responseConverter;
    private final String httpMethod;
    private final HttpUrl baseUrl;
    private final String relativeUrl;
    private final ParameterHandler<Object>[] parameterHandlers;
//    private final Headers headers;

    CallAdapter<R, T> callAdapter;

    ServiceMethod(Builder builder) {
        this.callFactory = builder.retrofit.callFactory();
        this.httpMethod = builder.httpMethod;
        this.baseUrl = builder.retrofit.baseUrl;
        this.relativeUrl = builder.relativeUrl;
        this.callAdapter = builder.callAdapter;
        this.parameterHandlers = builder.parameterHandlers;
        this.responseConverter = builder.responseConverter;
    }

    public Request toRequest(Object... args) {
        RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl);
        ParameterHandler<Object>[] handlers = this.parameterHandlers;
        int argumentCount = args != null ? args.length : 0;

        for (int i = 0; i < argumentCount; i++) {
            handlers[i].apply(requestBuilder,args);
        }
        return requestBuilder.build();
    }

    public R toResponse(ResponseBody body) {
        return responseConverter.convert(body);
    }

    public static final class Builder<T, R> {

        private static final String TAG = "ServiceMethod Builder";
        final Retrofit retrofit;
        final Method method;
        final Annotation[] methodAnnotations;
        final Annotation[][] parameterAnnotationsArray;
        final Type[] parameterTypes;
        ParameterHandler<?>[] parameterHandlers;
        Type responseType;
        CallAdapter<T, R> callAdapter;
        // T 指的是responseBody要被转换成的类型
        Converter<ResponseBody,T>responseConverter;

        String httpMethod;
        String relativeUrl;


        Builder(Retrofit retrofit, Method method) {
            this.retrofit = retrofit;
            this.method = method;
            this.methodAnnotations = method.getAnnotations();
            this.parameterAnnotationsArray = method.getParameterAnnotations();
            this.parameterTypes = method.getGenericParameterTypes();
        }

        public ServiceMethod build() {
            // 从 retrofit 对象里创建网络请求适配器
            callAdapter = createCallAdapter();
            responseType = callAdapter.returnType();

            responseConverter = createResponseConverter();

            for (Annotation annotation : methodAnnotations) {
                parseMethodAnnotation(annotation);
            }

            int parameterCount = parameterAnnotationsArray.length;
            parameterHandlers = new ParameterHandler[parameterCount];
            for (int i = 0; i < parameterCount; i++) {
                Type parameterType = parameterTypes[i];
                // 要把getRepo注解里的{user}去掉运行一次，然后添加回来，才能获取到annotations？
                Annotation[] parameterAnnotations = parameterAnnotationsArray[i];

                parameterHandlers[i] = parseParameter(i, parameterType, parameterAnnotations);
            }

            return new ServiceMethod(this);
        }

        private ParameterHandler<?> parseParameter(int i, Type parameterType, Annotation[] annotations) {
            ParameterHandler<?> result = null;
            for (Annotation annotation : annotations) {
                ParameterHandler<?> annotationAction = parseParameterAnnotation(
                        i, parameterType, annotations, annotation);
                result = annotationAction;
            }
            return result;
        }

        private ParameterHandler<?> parseParameterAnnotation(int i, Type parameterType, Annotation[] annotations, Annotation annotation) {
            if (annotation instanceof Path) {
                Path path = (Path) annotation;
                String name = path.value();
                Converter<?, String> converter = retrofit.stringConverter(parameterType, annotations);
                return new ParameterHandler.Path<>(name, converter, path.encoded());
            }
            return null;
        }

        private void parseMethodAnnotation(Annotation annotation) {
            if (annotation instanceof GET) {
                parseHttpMethodAndPath("GET", ((GET) annotation).value());
            }
        }

        private void parseHttpMethodAndPath(String httpMethod, String value) {
            this.httpMethod = httpMethod;
            this.relativeUrl = value;
        }


        private CallAdapter<T, R> createCallAdapter() {
            Type returnType = method.getGenericReturnType();
            Annotation[] annotations = method.getAnnotations();
            return (CallAdapter<T, R>) retrofit.callAdapter(annotations, returnType);
        }

        private Converter<ResponseBody,T> createResponseConverter(){
            Annotation[] annotations = method.getAnnotations();
            return retrofit.responseBodyConverter(responseType,annotations);
        }

        @Override
        public String toString() {
            return "Builder{" +
                    "retrofit=" + retrofit +
                    ", \nmethod=" + method +
                    ", \nmethodAnnotations=" + Arrays.toString(methodAnnotations) +
                    ", \nparameterAnnotationsArray=" + Arrays.toString(parameterAnnotationsArray) +
                    ", \nparameterType=" + Arrays.toString(parameterTypes) +
                    ", \ncallAdapter=" + callAdapter +
                    '}';
        }
    }
}
